`include "defines.v"
module bht#(
  parameter PHT_DEPTH = 1024
)(
  input clk  ,
  input rst_n,
  input [`VADDR_W-1:0] gen_bht_pc_i,
  output taken_o,

  input               update_valid_i    ,
  input               update_redict_i   ,
  input [`VADDR_W-1:0]update_redict_pc_i
);
  reg  [$clog2(PHT_DEPTH)-1:0] bhr;
  wire [$clog2(PHT_DEPTH)-1:0] pht_rd_addr;  
  wire [$clog2(PHT_DEPTH)-1:0] pht_wr_addr;
  wire pht_wen;
  wire [1:0]pht_din,pht_dout1,pht_dout2;

  pht#(
    .PHT_DEPTH(PHT_DEPTH)
  )PHT(
    .clk   (clk  ),
    .rst_n (rst_n),
    .wr_addr (pht_wr_addr),
    .rd_addr (pht_rd_addr),
    .wen  (pht_wen),
    .din  (pht_din),
    .dout1 (pht_dout1) ,
    .dout2 (pht_dout2) 
  );
  /* 预测路径 */
  // index生成
  assign pht_rd_addr =  bhr ^ gen_bht_pc_i[$clog2(PHT_DEPTH)+2-1:2];
  // taken 判断
  assign taken_o = (pht_dout1 == 2'b11) || (pht_dout1 == 2'b10) ? 1'b1 : 1'b0;
  /*-----------*/

  /* 更新路径 */
  //  BHR 的更新
  always@(posedge clk or negedge rst_n)
    if(~rst_n)
      bhr<= 'd0;
    else if(update_valid_i)
      bhr <= {bhr[$clog2(PHT_DEPTH)-2:0] , update_redict_i};

  // PHT 更新
  assign pht_wen     = update_valid_i;
  assign pht_wr_addr = bhr ^ update_redict_pc_i[$clog2(PHT_DEPTH)+2-1:2];
  assign pht_din     = update_redict_i ? pht_dout2 + 1'b1 : pht_dout2 - 1'b1;
  /*-----------*/
endmodule 

module pht#(
  parameter PHT_DEPTH = 1024
)(
  input clk,
  input rst_n,
  input [$clog2(PHT_DEPTH)-1:0] rd_addr,
  input [$clog2(PHT_DEPTH)-1:0] wr_addr,
  input wen,
  input [1:0]din,
  output[1:0]dout1,
  output[1:0]dout2
);
  reg [1:0] pht [0:PHT_DEPTH-1];

  genvar i;
  generate 
    for(i=0;i<PHT_DEPTH;i=i+1)begin
      always@(posedge clk or negedge  rst_n)
      if(~rst_n)
        pht[i] <= 2'b00;
      else if(wen && wr_addr == i)
        pht[i] <= din;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
    end
  endgenerate

  assign dout1 = pht[rd_addr];
  assign dout2 = pht[wr_addr];

endmodule